package com.changgou.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.changgou.common.vo.Result;
import com.changgou.goods.feign.SkuFeign;
import com.changgou.goods.pojo.Sku;
import com.changgou.search.dao.SkuEsMapper;
import com.changgou.search.pojo.SkuInfo;
import com.changgou.search.service.SkuEsService;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.swing.text.Highlighter;
import java.util.*;
import java.util.stream.Stream;

@Service
public class SkuEsServiceImpl implements SkuEsService {

    @Autowired
    private SkuFeign skuFeign;

    @Autowired
    private SkuEsMapper skuEsMapper;

    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

    /**
     * 导入sku商品到es
     * @return
     */
    @Override
    public void importSku() {
        //查找已审核的sku
        Result<List<Sku>> result = skuFeign.findByStatus("1");
        if (result.getData()!=null){
            //将sku列表转换为skuInfo列表
            List<SkuInfo> skuInfoList = JSON.parseArray(JSON.toJSONString(result.getData()), SkuInfo.class);

            //将spec规格字符串转换为specMap对象
            for (SkuInfo skuInfo : skuInfoList) {
                Map specMap = JSON.parseObject(skuInfo.getSpec(), Map.class);
                skuInfo.setSpecMap(specMap);
            }
            //导入es
            skuEsMapper.saveAll(skuInfoList);
        }

    }

    /**
     * 根据搜索条件搜索商品数据
     * @param searchMap
     * @return
     */
    @Override
    public Map search(Map<String, String> searchMap) {
        Map<String, Object> resultMap = new HashMap<>();
        //创建查询条件构造对象
        NativeSearchQueryBuilder queryBuilder=createQueryBuilder(searchMap);

        //条件查询
        /*AggregatedPage<SkuInfo> searchResult = elasticsearchTemplate.queryForPage(queryBuilder.build(), SkuInfo.class);*/

        AggregatedPage<SkuInfo> searchResult = searchSkuInfo(queryBuilder,searchMap);

        //处理分组统计查询
        groupSearch(queryBuilder,resultMap);

        //skuInfo列表
        resultMap.put("rows",searchResult.getContent());

        //总记录数
        resultMap.put("total",searchResult.getTotalElements());

        //总页数
        resultMap.put("totalPages",searchResult.getTotalPages());

        return resultMap;
    }

    private AggregatedPage<SkuInfo> searchSkuInfo(NativeSearchQueryBuilder queryBuilder, Map<String, String> searchMap) {
        if (!StringUtils.isEmpty(searchMap.get("keywords"))) {
            //如果有搜索关键字的时候才需要处理高亮
            //1、设置高亮域及标签；参数：要高亮的域名
            HighlightBuilder.Field field = new HighlightBuilder.Field("name");
            //设置高亮的起始标签
            field.preTags("<span style='color:red'>");
            //设置高亮的结束标签
            field.postTags("</span>");

            queryBuilder.withHighlightFields(field);

            //2、查询
            return elasticsearchTemplate.queryForPage(queryBuilder.build(), SkuInfo.class, new SearchResultMapper() {
                @Override
                public <T> AggregatedPage<T> mapResults(SearchResponse searchResponse, Class<T> aClass, Pageable pageable) {
                    //3、处理高亮返回结果
                    //高亮的记录列表
                    List<T> list = new ArrayList<>();
                    //获取商品
                    SearchHits hits = searchResponse.getHits();
                    for (SearchHit hit : hits) {
                        //skuInfo的json格式字符串
                        String skuInfoStr = hit.getSourceAsString();
                        SkuInfo skuInfo = JSON.parseObject(skuInfoStr, SkuInfo.class);

                        //获取高亮标题
                        HighlightField highlightField = hit.getHighlightFields().get("name");
                        if (highlightField != null) {
                            StringBuilder sb = new StringBuilder();
                            //所有高亮片段
                            Text[] fragments = highlightField.getFragments();
                            for (Text fragment : fragments) {
                                sb.append(fragment.toString());
                            }

                            //替换原名称为高亮的名称
                            skuInfo.setName(sb.toString());
                        }

                        //加入商品到列表中
                        list.add((T) skuInfo);
                    }

                    return new AggregatedPageImpl<>(list, pageable, searchResponse.getHits().getTotalHits());
                }
            });



        }
        return elasticsearchTemplate.queryForPage(queryBuilder.build(), SkuInfo.class);
    }


    private NativeSearchQueryBuilder createQueryBuilder(Map<String, String> searchMap) {

        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

        if (searchMap!=null){
            //根据搜索关键字搜索es中商品
            if (!StringUtils.isEmpty(searchMap.get("keywords"))){
                /**
                 * matchQuery 匹配查询，会对搜索的关键字进行分词
                 * 参数1：在es中文档的域名
                 * 参数2：搜索的值；如：小米手机
                 */
                queryBuilder.withQuery(QueryBuilders.matchQuery("name",searchMap.get("keywords")));
            }


            //因为后面会有很多的过滤查询条件；可以使用es的组合查询添加多个查询条件
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

            //分类过滤查询
            if (!StringUtils.isEmpty(searchMap.get("category"))){
                    boolQueryBuilder.must(QueryBuilders.matchQuery("categoryName",searchMap.get("category")));
            }

            //品牌过滤
            if (!StringUtils.isEmpty(searchMap.get("brand"))){
                boolQueryBuilder.must(QueryBuilders.matchQuery("brandName",searchMap.get("brand")));
            }

            //规格过滤
            for (Map.Entry<String, String> entry : searchMap.entrySet()) {
                if (entry.getKey().startsWith("spec_")){

                    //以spec_开头的就是规格过滤条件
                    String field = entry.getKey().substring(5);

                    boolQueryBuilder.must(QueryBuilders.matchQuery("specMap."+field+".keyword",entry.getValue()));
                }
            }

            //价格区间过滤
            if(!StringUtils.isEmpty(searchMap.get("price"))){
                //去掉多余字符  replaceAll
                String price = searchMap.get("price").replaceAll("元", "").replaceAll("以上", "");
                //分割  split
                String[] split = price.split("-");

                //查询大于等于价格下限
                boolQueryBuilder.must(QueryBuilders.rangeQuery("price").gte(split[0]));

                //查询小于价格上限
                boolQueryBuilder.must(QueryBuilders.rangeQuery("price").lt(split[1]));
            }



            //设置过滤查询
            queryBuilder.withFilter(boolQueryBuilder);

            //页号
            int pageNum=1;
            if (!StringUtils.isEmpty(searchMap.get("pageNum"))){
                pageNum=Integer.parseInt(searchMap.get("pageNum"));
            }

            //页大小
            int pageSize=2;
            if (!StringUtils.isEmpty(searchMap.get("pageSize"))){
                pageSize=Integer.parseInt(searchMap.get("pageSize"));
            }

            //设置分页
            /**
             * 设置分页
             * 参数一：页号：从0开始；0表示第一页，1表示第二页
             * 参数二：页大小
             */
            queryBuilder.withPageable(PageRequest.of(pageNum-1,pageSize));

            //排序
            if (!StringUtils.isEmpty(searchMap.get("sortField"))&&!StringUtils.isEmpty(searchMap.get("sortRule"))){
                queryBuilder.withSort(SortBuilders.fieldSort(searchMap.get("sortField"))
                        .order(SortOrder.valueOf(searchMap.get("sortRule"))));

            }

        }

        return queryBuilder;
    }


    /**
     * 对商品分类、品牌、规格进行分组统计
     * @param queryBuilder 查询条件构造对象
     * @param resultMap 返回结果
     */
    private void groupSearch(NativeSearchQueryBuilder queryBuilder, Map<String, Object> resultMap) {
        /**
         * 添加商品分类分组统计
         *参数1：给分组的结果起别名，随便起
         * 参数2：要进行分组统计的 域名
         */
        queryBuilder.addAggregation(AggregationBuilders.terms("skuCategoryName").field("categoryName"));

        /**
         * 添加商品品牌分组统计
         *参数1：给分组的结果起别名，随便起
         * 参数2：要进行分组统计的 域名
         */
        queryBuilder.addAggregation(AggregationBuilders.terms("skuBrandName").field("brandName"));

        /**
         * 添加商品规格分组统计
         *参数1：给分组的结果起别名，随便起
         * 参数2：要进行分组统计的 域名
         */
        queryBuilder.addAggregation(AggregationBuilders.terms("skupec").field("spec.keyword"));

        //查询
        AggregatedPage<SkuInfo> aggregatedPage = elasticsearchTemplate.queryForPage(queryBuilder.build(), SkuInfo.class);

        //获取所有分组
        Aggregations aggregations = aggregatedPage.getAggregations();


        //**************************************************************************
        //获取 商品分类 对应的分组数据
        StringTerms categoryStringTerms = aggregations.get("skuCategoryName");

        //创建返回结果
        List<String> categoryList=new ArrayList<>();
        for (StringTerms.Bucket bucket : categoryStringTerms.getBuckets()) {
            categoryList.add(bucket.getKeyAsString());
        }
        resultMap.put("categoryList",categoryList);


        //**************************************************************************
        //获取 商品品牌 对应的分组数据
        StringTerms brandStringTerms = aggregations.get("skuBrandName");

        //创建返回结果
        List<String> brandList=new ArrayList<>();
        for (StringTerms.Bucket bucket : brandStringTerms.getBuckets()) {
            brandList.add(bucket.getKeyAsString());
        }
        resultMap.put("brandList",brandList);


        //**************************************************************************
        //获取 商品规格 对应的分组数据
        StringTerms specStringTerms = aggregations.get("skupec");

        //创建返回结果
        Map<String, Set<String>> specMap=new HashMap<>();
        for (StringTerms.Bucket bucket : specStringTerms.getBuckets()) {
            //将每个键名转换为一个map对象-->{"颜色":"橙色","机身内存":"128G"}
            Map<String,String> map = JSON.parseObject(bucket.getKeyAsString(), Map.class);

            //2.2、对上述的map对象的每个key进行遍历
            for (Map.Entry<String, String> entry : map.entrySet()) {
                //判断返回的map中是否包含当前的规格，如果不包含则添加key到map中并设置一个集合作为其值
                if (!specMap.containsKey(entry.getKey())){
                    specMap.put(entry.getKey(),new HashSet<>());
                }

                //当key存在在返回的map中的时候，则将当前的key对应的值加入到返回map中的key对应的集合中
                specMap.get(entry.getKey()).add(entry.getValue());
            }
        }
        resultMap.put("specMap",specMap);
        //**************************************************************************

    }
}
